/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.master.scheduler;

import org.openislands.oi.error.UidGenerateException;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Component
public class JobIdGenerator {
    private long lastSecond;
    private long sequence;
    private final long maxSequence;
    private final SimpleDateFormat dateFormat;

    public JobIdGenerator(){
       this(6);
    }

    public JobIdGenerator(int sequenceBits){
        this.sequence = 0;
        this.lastSecond = -1;
        this.maxSequence = ~(-1L << sequenceBits);
        this.dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    }

    public synchronized String nextId() {
        long currentSecond = this.getCurrentSecond();
        if (currentSecond < this.lastSecond) {
            throw new UidGenerateException("clock moved backwards");
        } else {
            if (currentSecond == this.lastSecond) {
                this.sequence = this.sequence + 1 & this.maxSequence;
                if (this.sequence == 0) {
                    currentSecond = this.getNextSecond(this.lastSecond);
                }
            } else {
                this.sequence = 1;
            }
            this.lastSecond = currentSecond;
            return String.format("%s%d", this.dateFormat.format(new Date(currentSecond*1000)), this.sequence);
        }
    }

    private long getNextSecond(long lastTimestamp) {
        long timestamp;
        while (true){
            timestamp = this.getCurrentSecond();
            if (timestamp > lastTimestamp){
                return timestamp;
            }
        }
    }

    private long getCurrentSecond() {
        return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
    }
}
